package org.flexthinker.binding.bean
{
    import mx.events.PropertyChangeEvent;

    import org.flexthinker.binding.value.PropertyChangeListener;
    import org.flexthinker.binding.value.ValueModel;
    import org.flexthinker.binding.value.support.DelegatingPropertyChangeListener;
    import org.flexthinker.binding.value.support.ValueHolder;

    /**
     * Base class for <code>BeanProperty</code> and <code>ElProperty</code>.
     * It manages a list of <code>PropertyChangeListener</code>s,
     * listens to a certain ValueModel and then notifies all
     * listeners when that ValueModel has changed.
     *
     * @author Dragos Dascalita Haut
     */
    public class PropertyHelper implements ValueModel
    {
        private var _source:Object;
        private var _path:String;

        protected var valueModelsInPath:Vector.<ValueModel> = new Vector.<ValueModel>();

        protected var changeListeners:Vector.<PropertyChangeListener> = new Vector.<PropertyChangeListener>();

        public function PropertyHelper(source:Object, path:String):void
        {
            if (! path)
            {
                throw new Error("Must provide a path to a property");
            }
            _path = path;

            if (source)
            {
                setSource(source);
            }
        }

        public function setSource(value:Object):void
        {
            if (value == null)
            {
                return;
            }
            _source = value;

        }

        public function getSource():Object
        {
            return _source;
        }

        public function getPath():String
        {
            return _path;
        }

        public function getValue():Object
        {
            throw new Error("getValue() method should have been overridden in the super class.");
            return null;
        }


        public function setValue(value:Object):void
        {
            throw new Error("setValue() method should have been overridden in the super class.")
        }


        public function addValueChangeListener(listener:PropertyChangeListener):void
        {
            if (listener == null || changeListeners.indexOf(listener) >= 0)
            {
                return;
            }

            changeListeners.push(listener);
        }


        public function removeValueChangeListener(listener:PropertyChangeListener):void
        {
            var idx:int = changeListeners.indexOf(listener);
            if (idx >= 0)
            {
                changeListeners.splice(idx, 1);
            }
        }


        public function removeAllListeners():void
        {
            changeListeners = new Vector.<PropertyChangeListener>();
        }


        public function equals(obj:Object):Boolean
        {
            throw new Error("Not allowed to call equals");
        }

        // --------------------
        //   internal methods
        // ---------------------


        protected function resetWatchers():void
        {
            for each (var vm:ValueModel in valueModelsInPath)
            {
                vm.removeAllListeners();
            }
            valueModelsInPath = new Vector.<ValueModel>();
        }

        protected function createValueModel(subpathHost:Object, subpathPropertyName:String):ValueModel
        {
            var vh:ValueHolder = new ValueHolder(subpathHost, subpathPropertyName);
            valueModelsInPath.push(vh);
            return vh;
        }

        protected function listenTo(vm:ValueModel):void
        {
            vm.addValueChangeListener(new DelegatingPropertyChangeListener(subPropertyChangedHandler));
        }

        protected function subPropertyChangedHandler(event:PropertyChangeEvent):void
        {
            notifyAllListeners(event);
        }

        private function notifyAllListeners(event:PropertyChangeEvent):void
        {
            for each (var listener:PropertyChangeListener in changeListeners)
            {
                listener.propertyChange(event);
            }
        }

    }
}